﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ComputerArchitecture.Core;
using ComputerArchitecture.Core.Logging;
using ComputerArchitecture.Numbers;
using ComputerArchitecture.Units.Integers.Unsigned.Interfaces;

namespace ComputerArchitecture.Units.Integers.Unsigned.Implementations
{
    public class UnsignedDivideUnit : LoggingObject, IUnsignedDivideUnit
    {
        #region Fields

        private readonly IUnsignedCompareUnit _unsignedCompareUnit;
        private readonly IUnsignedSubstractUnit _unsignedSubstractUnit;

        #endregion

        #region Constructors

        public UnsignedDivideUnit(ILoggingService loggingService, IUnsignedCompareUnit unsignedCompareUnit, IUnsignedSubstractUnit unsignedSubstractUnit)
            : base(loggingService)
        {
            ArgumentValidator.NullValidate(unsignedCompareUnit, "unsignedCompareUnit");
            _unsignedCompareUnit = unsignedCompareUnit;
            ArgumentValidator.NullValidate(unsignedSubstractUnit, "unsignedSubstractUnit");
            _unsignedSubstractUnit = unsignedSubstractUnit;
        }

        public UnsignedDivideUnit(ILoggingService loggingService)
            : this(loggingService, new UnsignedCompareUnit(loggingService), new UnsignedSubstractUnit(loggingService) )
        {

        }

        #endregion

        #region Methods

        IBitNumber IDivideUnit.Divide(IBitNumber dividend, IBitNumber divisor, out IBitNumber remainder)
        {
            IUnsignedBitNumber rem;
            var result = this.Divide((IUnsignedBitNumber) dividend, (IUnsignedBitNumber) divisor, out rem);
            remainder = rem;
            return result;
        }

        public IBitNumber Divide(IBitNumber dividendMoreSignificant, IBitNumber dividendLessSignificant, IBitNumber divisor, out IBitNumber remainder)
        {
            throw new NotImplementedException();
        }

        public IUnsignedBitNumber Divide(IUnsignedBitNumber dividend, IUnsignedBitNumber divisor, out IUnsignedBitNumber remainder)
        {
            ArgumentValidator.NullValidate(dividend, "dividend");
            ArgumentValidator.NullValidate(divisor, "divisor");
            if (dividend.Size != divisor.Size)
            {
                throw new IncompatibleSizeException();
            }
            if (divisor.IsZero)
            {
                throw new DivideByZeroException();
            }

            IUnsignedBitNumber quotient = new UnsignedBitNumber(dividend.Size);
            remainder = new UnsignedBitNumber(dividend.Size);
            
            for (int i = 0; i < dividend.Size - 1; ++i)
            {
                Bit carryBit;
                remainder.LogicShiftLeft(1, out carryBit);
                remainder[dividend.Size - 1].SetAs(dividend[i]);
                if (_unsignedCompareUnit.Compare(remainder, divisor) != -1)
                {
                    bool overflow;
                    remainder = _unsignedSubstractUnit.Substract(remainder, divisor, out overflow);
                    quotient[i].Set();
                }
            }
            return quotient;
        }

        public IUnsignedBitNumber Divide(IUnsignedBitNumber dividendMoreSignificant, IUnsignedBitNumber dividendLessSignificant, IUnsignedBitNumber divisor)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Division without restoring remainder
        /// </summary>
        /// <param name="dividend"></param>
        /// <param name="divisor"></param>
        /// <returns></returns>
        public IUnsignedBitNumber Divide(IUnsignedBitNumber dividend, IUnsignedBitNumber divisor)
        {
            IUnsignedBitNumber remainder;
            return this.Divide(dividend, divisor, out remainder);
        }

        #endregion
    }
}
